#pragma once

#include <AVUIFreezable.h>
#include <AVUIIAnimatable.h>

namespace AVUI {

class AnimationTimeline;

/// <summary>Abstract class that provides animation support. </summary>
class Animatable : public Freezable, public IAnimatable
{
public:
    DECLARE_ELEMENT(Animatable, Freezable);

#pragma region Public Methods

    /// <summary>Applies an <see cref="T:System.Windows.Media.Animation.AnimationClock" /> to the specified <see cref="T:System.Windows.DependencyProperty" />. If the property is already animated, the <see cref="F:System.Windows.Media.Animation.HandoffBehavior.SnapshotAndReplace" /> handoff behavior is used.</summary>
    /// <param name="dp">The property to animate.</param>
    /// <param name="clock">The clock with which to animate the specified property. If <paramref name="clock" /> is null, all animations will be removed from the specified property (but not stopped). </param>
    void ApplyAnimationClock(DependencyProperty* pDP, AnimationClock* pClock);

    /// <summary>Applies an <see cref="T:System.Windows.Media.Animation.AnimationClock" /> to the specified <see cref="T:System.Windows.DependencyProperty" />. If the property is already animated, the specified <see cref="T:System.Windows.Media.Animation.HandoffBehavior" /> is used.</summary>
    /// <param name="dp">The property to animate.</param>
    /// <param name="clock">The clock with which to animate the specified property. If <paramref name="handoffBehavior" /> is <see cref="F:System.Windows.Media.Animation.HandoffBehavior.SnapshotAndReplace" /> and <paramref name="clock" /> is null, all animations will be removed from the specified property (but not stopped). If <paramref name="handoffBehavior" /> is <see cref="F:System.Windows.Media.Animation.HandoffBehavior.Compose" /> and clock is null, this method has no effect.</param>
    /// <param name="handoffBehavior">A value that specifies how the new animation should interact with any current animations already affecting the property value.</param>
    void ApplyAnimationClock(DependencyProperty* pDP, AnimationClock* pClock, HandoffBehavior::Enum handoffBehavior);

    /// <summary>Applies an animation to the specified <see cref="T:System.Windows.DependencyProperty" />. The animation is started when the next frame is rendered. If the specified property is already animated, the <see cref="F:System.Windows.Media.Animation.HandoffBehavior.SnapshotAndReplace" /> handoff behavior is used.</summary>
    /// <param name="dp">The property to animate.</param>
    /// <param name="animation">The animation used to animate the specified property.If the animation's <see cref="P:System.Windows.Media.Animation.Timeline.BeginTime" /> is null, any current animations will be removed and the current value of the property will be held.If <paramref name="animation" /> is null, all animations will be removed from the property and the property value will revert back to its base value.</param>
    void BeginAnimation(DependencyProperty* pDP, AnimationTimeline* pAnimation);

    /// <summary>Applies an animation to the specified <see cref="T:System.Windows.DependencyProperty" />. The animation is started when the next frame is rendered. If the specified property is already animated, the specified <see cref="T:System.Windows.Media.Animation.HandoffBehavior" /> is used. </summary>
    /// <returns />
    /// <param name="dp">The property to animate.</param>
    /// <param name="animation">The animation used to animate the specified property.If <paramref name="handoffBehavior" /> is <see cref="F:System.Windows.Media.Animation.HandoffBehavior.SnapshotAndReplace" /> and the animation's <see cref="P:System.Windows.Media.Animation.Timeline.BeginTime" /> is null, any current animations will be removed and the current value of the property will be held. If <paramref name="handoffBehavior" /> is <see cref="F:System.Windows.Media.Animation.HandoffBehavior.SnapshotAndReplace" /> and <paramref name="animation" /> is a null reference, all animations will be removed from the property and the property value will revert back to its base value.If <paramref name="handoffBehavior" /> is <see cref="F:System.Windows.Media.Animation.HandoffBehavior.Compose" />, this method will have no effect if the animation or its <see cref="P:System.Windows.Media.Animation.Timeline.BeginTime" /> is null.</param>
    /// <param name="handoffBehavior">A value that specifies how the new animation should interact with any current animations already affecting the property value.</param>
    void BeginAnimation(DependencyProperty* pDP, AnimationTimeline* pAnimation, HandoffBehavior::Enum handoffBehavior);

    /// <summary>Creates a modifiable clone of this <see cref="T:System.Windows.Media.Animation.Animatable" />, making deep copies of this object's values. When copying this object's dependency properties, this method copies resource references and data bindings (but they might no longer resolve) but not animations or their current values. </summary>
    /// <returns>A modifiable clone of this instance. The returned clone is effectively a deep copy of the current object. The clone's <see cref="P:System.Windows.Freezable.IsFrozen" /> property is false.</returns>
    TRefCountedPtr<Animatable> Clone() { return (Animatable*)Freezable::Clone().get_Ptr(); }

    /// <summary>Returns the non-animated value of the specified <see cref="T:System.Windows.DependencyProperty" />.</summary>
    /// <returns>The value that would be returned if the specified property were not animated. </returns>
    /// <param name="dp">Identifies the property whose base (non-animated) value should be retrieved. </param>
    Object* GetAnimationBaseValue(DependencyProperty* pDP);

#pragma endregion Public Methods

#pragma region Public Properties

    /// <summary>Gets a value that indicates whether one or more <see cref="T:System.Windows.Media.Animation.AnimationClock" /> objects is associated with any of this object's dependency properties.</summary>
    /// <returns>true if one or more <see cref="T:System.Windows.Media.Animation.AnimationClock" /> objects is associated with any of this object's dependency properties; otherwise, false.</returns>
    bool get_HasAnimatedProperties();

#pragma endregion Public Properties

protected:

#pragma region Protected Methods

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Media.Animation.Animatable" /> class.</summary>
    Animatable() { }

    /// <summary>Makes this <see cref="T:System.Windows.Media.Animation.Animatable" /> object unmodifiable or determines whether it can be made unmodifiable.</summary>
    /// <returns>If <paramref name="isChecking" /> is true, this method returns true if this <see cref="T:System.Windows.Media.Animation.Animatable" /> can be made unmodifiable, or false if it cannot be made unmodifiable. If <paramref name="isChecking" /> is false, this method returns true if the if this <see cref="T:System.Windows.Media.Animation.Animatable" /> is now unmodifiable, or false if it cannot be made unmodifiable, with the side effect of having begun to change the frozen status of this object.</returns>
    /// <param name="isChecking">true if this method should simply determine whether this instance can be frozen. false if this instance should actually freeze itself when this method is called.</param>
    virtual bool FreezeCore(bool isChecking) AVUI_OVERRIDE;

#pragma endregion Protected Methods

public:

#pragma region Internal Methods

    virtual void EvaluateAnimatedValueCore(DependencyProperty* pDP, PropertyMetadata* pMetadata, EffectiveValueEntry* pNewEntry) AVUI_OVERRIDE;
    void InternalWritePreamble() { Freezable::WritePreamble(); }
    bool IsBaseValueDefault(DependencyProperty* pDP);
    virtual void Precompute() { };
    void PropertyChanged(DependencyProperty* pDP);
    static TRefCountedPtr<DependencyProperty> RegisterProperty(char* szName, const Type& propertyType, const Type& ownerType, Object* pDefaultValue, PropertyChangedCallback changed, ValidateValueCallback validate, bool isIndependentlyAnimated, CoerceValueCallback coerced);

#pragma endregion Internal Methods

private:

};

typedef TRefCountedPtr<Animatable> AnimatablePtr;

}; // namespace AVUI
